<?php

abstract class BaseOrm {
	public $Type;

	// TODO: put in config
	protected static $_orm_config = array(
		'Blog' => array(
			'EssentialColumns' => array(
				'blog.id',
				'blog.dname',
				),
			'PrimaryColumn' => 'id',
			'TableName' => 'msnu_blog',
			'TableAlias' => 'blog',
			'HasMany' => array(
				'Post' => 'blog_id',
				),
		),
		'Post' => array(
			'EssentialColumns' => array(
				'post.id',
				'post.title',
				'post.body',
				'UNIX_TIMESTAMP(post.created) AS created',
				'post.usr_id',
				),
			'TableName' => 'msnu_post',
			'TableAlias' => 'post',
			'PrimaryColumn' => 'id',
			'HasMany' => array(
				'Post' => 'post_id',
				),
			'BelongsTo' => array(
				'User' => 'usr_id',
				'Blog' => 'blog_id',
				'Post' => 'post_id',
				),
		),
		'Smiley' => array(
			'EssentialColumns' => array(
				'smiley.id',
				'smiley.title',
				'smiley.description',
				'smiley.path',
				'smiley.usr_id',
			),		
			'HasMany' => array(
				'Tag' => 'smiley_id',
				'Post' => 'smiley_id',
			),
			'BelongsTo' => array(
				'User' => 'usr_id',
			),
			'PrimaryColumn' => 'id',
			'TableName' => 'msnu_smiley',
			'TableAlias' => 'smiley',
		),
		'Tag' => array(
			'EssentialColumns' => array(
				'tag.id',
				'tag.dname',
				'tag.dimage',
				),
			'TableName' => 'msnu_sgenre',
			'TableAlias' => 'tag',
			'PrimaryColumn' => 'id',
		),
		'User' => array(
			'EssentialColumns' => array(
				'user.id',
				'user.urlname',
				'user.dname',
				'user.pswd'
			),
			'PrimaryColumn' => 'id',
			'TableName' => 'msnu_usr',
			'TableAlias' => 'user',
			'HasMany' => array(
				'Smiley' => 'usr_id',
			),
		),
	);

	public static function GetEssentialColumns($type) {
		return implode(', ', self::$_orm_config[$type]['EssentialColumns']);
	}

	public static function GetPrimaryColumn($type) {
		return self::$_orm_config[$type]['PrimaryColumn'];
	}

	public static function GetTableName($type) {
		return self::$_orm_config[$type]['TableName'];
	}

	public static function GetTableAlias($type) {
		return self::$_orm_config[$type]['TableAlias'];
	}

	public static function GetHasMany($type) {
		return self::$_orm_config[$type]['HasMany'];
	}

	public static function GetHasOne($type) {
		return self::$_orm_config[$type]['HasOne'];
	}

	public static function GetBelongsTo($type) {
		return self::$_orm_config[$type]['BelongsTo'];
	}

	public $Values;

	public function __construct($values = NULL) {
		if ( is_array($values) ) {
			$this->Values = $values;
		} elseif ( intval($values) ) {
			$this->SetValuesByPrimaryKey(intval($values));
		}
	}

	public function EssentialColumns() {
		return @BaseOrm::GetEssentialColumns($this->Type);
	}

	public function PrimaryColumn() {
		return @BaseOrm::GetPrimaryColumn($this->Type);
	}

	public function TableName() {
		return @BaseOrm::GetTableName($this->Type);
	}

	public function TableAlias() {
		return @BaseOrm::GetTableAlias($this->Type);
	}

	public function HasMany() {
		return @BaseOrm::GetHasMany($this->Type);
	}

	public function HasOne() {
		return @BaseOrm::GetHasOne($this->Type);
	}

	public function BelongsTo() {
		return @BaseOrm::GetBelongsTo($this->Type);
	}

	public function SetValuesByPrimaryKey($pk) {
		$query = "
			/* " . __FILE__ . "::" . __LINE__ . " */
			SELECT
				" . $this->EssentialColumns() . "
			FROM
				" . $this->TableName() . " AS " . $this->TableAlias() . "
			WHERE
				" . $this->TableAlias() . '.' . $this->PrimaryColumn() . "=" . intval($pk) . "
				AND enabled=1
			LIMIT
				1
		";

		return $this->_set($query);
	}

	protected function _set($query) {
		$result = Dbi::GetResult($query);

		// TODO: exception
		if ( !$result || !mysql_num_rows($result) ) return false;

		$row = mysql_fetch_assoc($result);

		$this->Values = $row;
		return true;
	}

	// Object methods
	public function User() {
		return $this->_getOne('User');
	}

	public function Smiley() {
		return $this->_getOne('Smiley');
	}

	public function Smileys($offset = 0, $limit = NULL, $method = NULL, $column = NULL) {
		return $this->_getMany('Smiley', $offset, $limit);
	}

	public function SmileyTags($offset = 0, $limit = NULL) {
		return $this->_getMany('SmileyTag', $offset, $limit);
	}

	public function Tags($offset = 0, $limit = NULL) {
		return $this->_getMany('Tag', $offset, $limit);
	}

	// Posts
	protected $_post_offset = 0;
	protected $_post_limit = 10;
	public function SetPostOffset($value) {
		$this->_post_offset = $value;
	}
	public function SetPostLimit($value) {
		$this->_post_limit = $value;
	}
	public function Posts($offset = NULL, $limit = NULL) {
		$offset = ($offset === NULL && isset($this->_post_offset))?$this->_post_offset:$offset;
		$limit = ($limit === NULL && isset($this->_post_limit))?$this->_post_limit:$limit;
		return $this->_getMany('Post', $offset, $limit);
	}
	public function PostPagination() {
		return $this->Posts()->Pagination();
	}

	protected $_manys = array();
	protected function _getMany($type, $offset, $limit) {
		$HasMany = $this->HasMany();
		// TODO: Exception
		if ( !isset($HasMany[$type]) ) return NULL;
		if ( !isset($this->_manys[$type]) ) {
			$fk_column = $HasMany[$type];
			$this_pk_value = $this->Values[$this->PrimaryColumn()];
			$list = $type . 'List';
			$list_obj = new $list($offset, $limit, $this);

			$this->_manys[$type] = $list_obj;
		}

		return $this->_manys[$type];
	}

	protected $_belongs_tos = array();
	protected $_has_ones = array();
	protected function _getOne($type) {
		$HasOne = $this->HasOne();
		$BelongsTo = $this->BelongsTo();
		if ( isset($HasOne[$type]) ) {
			$column = $HasOne[$type];
			$value = $this->Values[$HasOne[$type]];
	
			if ( isset($_has_ones[$column]) ) return $_has_ones[$column];
	
			// TODO: check for null
			$obj = new $type($value);
			$_has_ones[$column] = $obj;
			return $obj;
		} elseif ( isset($BelongsTo[$type]) ) {
			$column = $BelongsTo[$type];
			$value = $this->Values[$BelongsTo[$type]];
	
			if ( isset($_belongs_tos[$column]) ) return $_belongs_tos[$column];
	
			// TODO: check for null
			$obj = new $type($value);
			$_belongs_tos[$column] = $obj;
			return $obj;
		} else {
			// TODO: Exception
			return NULL;
		}
	}
}

abstract class BaseOrmList {
	public $Type;
	public $Values = array();
	public $TotalCount;
	public $Parent;
	protected $_offset;
	protected $_limit;

	public function __construct($offset = 0, $limit = 10, $parent) {
		$this->_offset = $offset;
		$this->_limit = $limit;
		if ( is_object($parent) ) {
			$this->SetValuesByParent($parent, $offset, $limit);
		}
	}

	protected $_pagination;
	public function Pagination() {
		// TODO: Exception
		$page_number = (intval($this->_offset) / $this->_limit) + 1;		

		$pagination = Monkake::GetObject('Pagination',
			$this->TotalCount, // item count
			$page_number, // current page
			$this->_limit, // items per page
			'/view_posts/?type=' . get_class($this->Parent) . '&id=' . $this->Parent->Id() . '&pnum={number}', // link
			5, // jump
			3, // previous pages
			3, // next pages 
			2, // first pages
			2, // last pages
			TRUE // span
			);

		$this->_pagination = $pagination->Paginate();

		return $this->_pagination;
	}

	// Static
	public function SetValuesByParent($parent, $offset = 0, $limit = NULL) {
		$this->_offset = $offset;
		$this->_limit = $limit;
		$this->Parent = $parent;
		$table_name = BaseOrm::GetTableName($this->Type);
		$table_alias = BaseOrm::GetTableAlias($this->Type);
		$essential_columns = BaseOrm::GetEssentialColumns($this->Type);
		$HasMany = $parent->HasMany();
		$fk_column = $HasMany[$this->Type];
		$id = $parent->Values[BaseOrm::GetPrimaryColumn($parent->Type)];

		$count_query = "
			/* " . __FILE__ . "::" . __LINE__ . " */
			SELECT
				COUNT(*) AS c
			FROM
				$table_name AS $table_alias
			WHERE
				$table_alias.$fk_column = " . intval($id) . " AND
				$table_alias.enabled=1
			";

		$query = "
			/* " . __FILE__ . "::" . __LINE__ . " */
			SELECT
				$essential_columns
			FROM
				$table_name AS $table_alias
			WHERE
				$table_alias.$fk_column = " . intval($id) . " AND
				$table_alias.enabled=1
			ORDER BY
				$table_alias.id DESC
			" . (($limit)?"
			LIMIT
				" . intval($offset) . ", " . intval($limit):"");

		return $this->_set($offset, $limit, $query, $count_query);
	}

	protected function GetEssentialColumns() {
		return BaseOrm::GetEssentialColumns($this->Type);
	}

	protected function GetTableAlias() {
		return BaseOrm::GetTableAlias($this->Type);
	}

	protected function GetTableName() {
		return BaseOrm::GetTableName($this->Type);
	}

	protected function _set($offset, $limit, $query, $count_query = NULL) {
		$values = array();

		if ( $count_query ) {
			$count_result = Dbi::GetResult($count_query);

			if ( (!$row_count = mysql_fetch_assoc($count_result)) || !intval($row_count['c']) ) return false;
	
			$this->TotalCount = $row_count['c'];
		}

		$result = Dbi::GetResult($query);

		while ( $row = mysql_fetch_assoc($result) ) {
			$o = new $this->Type($row);
			$this->Values[] = $o;
		}

		return true;
	}
}